#include	"../include/Application.h"
#include	"../include/Logger.h"
#include	"../include/OS.h"
#include	"../include/Tickable.h"

#if defined(_WIN32)
#	include		<Windows.h>
#	pragma		comment(lib, "user32.lib")
#else
#	include		<sys/resource.h>
#	include		<unistd.h>
#endif

#include	<csignal>
#include	<thread>

static Application * app = nullptr;
static void QuitApp(int sig) { if (app) app->Stop(sig); }

Application::Application() : _is_running(false), _exit_code(0), _frame_time(0) {
	if (!app) {
		app = this;
	} else {
		throw std::runtime_error("There is more than one application instance!");
	}
}

int Application::Start(int argc, char * argv[]) {
	OS::ChangeWorkDir(OS::GetDirName(argv[0]));

#if !defined(_WIN32)
	struct rlimit core;
	getrlimit(RLIMIT_CORE, &core);
	core.rlim_cur = RLIM_INFINITY;
	core.rlim_max = RLIM_INFINITY;
	setrlimit(RLIMIT_CORE, &core);
	signal(SIGPIPE, SIG_IGN);
#endif

	bool is_daemon = false;
	{
		Commandline cmd(argc, argv);
		is_daemon = cmd.Has("--daemon");
		if (!OnInit(cmd)) return 0;
	}

	if (is_daemon) {
	#if defined(_WIN32)
		HWND hWnd = FindWindowA("ConsoleWindowClass", NULL);
		if (hWnd) ShowWindow(hWnd, SW_HIDE);
	#else
		(void)daemon(1, 0);
	#endif
	}

	signal(SIGINT, &QuitApp);
	signal(SIGTERM, &QuitApp);
	signal(SIGABRT, &QuitApp);

	_is_running = true;
	if (_frame_time > 0) {
		double next = OS::Tick() + _frame_time;
		while (_is_running) {
			ITickable::BreathAll();
			OnBreath();

			double left = next - OS::Tick();
			if (left > 0) {
				std::this_thread::sleep_for(std::chrono::milliseconds((int)(left + 1)));
			} else if (left < -_frame_time) {
				LOG_WARN("Frame delay : %.4lfms", (-left));
			}

			next += _frame_time;
		}
	} else {
		std::chrono::milliseconds wait(10);

		while (_is_running) {
			ITickable::BreathAll();
			OnBreath();
			std::this_thread::sleep_for(wait);
		}
	}

	OnExit();
	return 0;
}

void Application::Stop(int code) {
	_exit_code = code;
	_is_running = false;
}

void Application::LockFPS(int fps) {
	if (fps <= 0) return;
	_frame_time = 1000.0 / fps;
}